home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 January / macformat-020.iso / Shareware City / Developers / OutOfPhase1.01Source / OutOfPhase Folder / Level 0 Macintosh 07Aug94 / EventLoop.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-01  |  29.0 KB  |  993 lines  |  [TEXT/KAHL]

  1. /* EventLoop.c */
  2. /*****************************************************************************/
  3. /*                                                                           */
  4. /*    System Dependency Library for Building Portable Software               */
  5. /*    Macintosh Version                                                      */
  6. /*    Written by Thomas R. Lawrence, 1993 - 1994.                            */
  7. /*                                                                           */
  8. /*    This file is Public Domain; it may be used for any purpose whatsoever  */
  9. /*    without restriction.                                                   */
  10. /*                                                                           */
  11. /*    This package is distributed in the hope that it will be useful,        */
  12. /*    but WITHOUT ANY WARRANTY; without even the implied warranty of         */
  13. /*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                   */
  14. /*                                                                           */
  15. /*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
  16. /*                                                                           */
  17. /*****************************************************************************/
  18.  
  19. #include "MiscInfo.h"
  20. #include "Debug.h"
  21. #include "Audit.h"
  22. #include "Definitions.h"
  23.  
  24. #pragma options(pack_enums)
  25. #include <AppleEvents.h>
  26. #include <Events.h>
  27. #include <Windows.h>
  28. #include <Menus.h>
  29. #include <Quickdraw.h>
  30. #include <Desk.h>
  31. #include <ToolUtils.h>
  32. #include <GestaltEqu.h>
  33. #include <Power.h>
  34. #pragma options(!pack_enums)
  35.  
  36. #include "MyMalloc.h"
  37. #include "EventLoop.h"
  38. #include "Memory.h"
  39. #include "Menus.h"
  40.  
  41.  
  42. /* how many 1/60ths of a second occur between mouse image update events */
  43. #define CURSORUPDATEDELAY (15)
  44.  
  45. /* how many 1/60ths of a second should we leave menu hilited */
  46. #define MENUDELAY (4)
  47.  
  48. /* how many 1/60ths of a second between WaitNextEvents() when calling */
  49. /* RelinquishCPUJudiciously while application is in the foreground. */
  50. #define JUDICIOUSDELAYFOREGROUND (19)
  51.  
  52. /* same as JUDICIOUSDELAYFOREGROUND, except for when application is in background */
  53. #define JUDICIOUSDELAYBACKGROUND (5)
  54.  
  55. /* timeout factor for event loop when mouse is down (to facilitate mouse tracking) */
  56. #define VERYSMALLTIMEINTERVAL (1)
  57.  
  58. /* what timeout should RelinquishCPU (not judiciously) use */
  59. #define RELINQUISHCPUNORMALDELAY (5)
  60.  
  61. /* how long should we beep or invert the menu bar */
  62. #define BEEPDURATION (20)
  63.  
  64. /* possible states that the menu handling stuff could be in */
  65. typedef enum
  66.     {
  67.         eNoMenu EXECUTE(= -8764),
  68.         eMenuPendingMouse,
  69.         eMenuPendingKey,
  70.         eMenuSelected
  71.     } MenuStates;
  72.  
  73.  
  74. /* NIL = no mouse down; otherwise, it's the window that the mouse went down in */
  75. /* so that the mouse up event can be reported to the same window. */
  76. static WindowPtr            LastMouseDownInThisWindow = NIL;
  77.  
  78. /* when did we last check the mouse cursor image. */
  79. static long                        LastCursorCheck = 0;
  80.  
  81. /* when was the last time we actually waited for an event (this is used by */
  82. /* RelinquishCPUJudiciously to keep from doing it too often) */
  83. static long                        LastEventTime = 0;
  84.  
  85. /* what is the current timeout for WaitNextEvent */
  86. static long                        SleepTime = CURSORUPDATEDELAY;
  87.  
  88. /* what window was the last one that was active (for detecting changes) */
  89. static WindowPtr            LastActiveWindow = NIL;
  90.  
  91. /* flag indicating whether we are in the foreground or not */
  92. static MyBoolean            RunningInForeground = True;
  93.  
  94. /* sticky flag that remembers if the user tried to cancel during RelinquishCPU */
  95. static MyBoolean            CancelPending = False;
  96.  
  97. /* buffer for keys received during RelinquishCPU.  if it is NIL, then the buffer */
  98. /* does not exist. */
  99. static EventRecord*        KeyboardEventBuffer = NIL;
  100.  
  101. /* this is used during RelinquishCPUJudiciously to keep PowerBooks from */
  102. /* going into idle state */
  103. static MyBoolean            IsThisAPowerBook = False;
  104.  
  105. /* current delay for RelinquishCPUJudiciously.  It depends on RunningInForeground */
  106. static long                        JudiciousInterval = JUDICIOUSDELAYFOREGROUND;
  107.  
  108. /* flag that tells whether we should make beeping noises or not */
  109. static MyBoolean            MakeErrorBeeps = True;
  110.  
  111.  
  112. #if (CURRENTPROCTYPE == PROC68000) || (CURRENTPROCTYPE == PROC68020)
  113.     /* efficiency hack for 680x0 Macs */
  114.     #define TickCount() (*((volatile unsigned long*)0x016a))
  115. #else
  116.     /* 'nicer' for PowerPC */
  117. #endif
  118.  
  119.  
  120. /* initialize internal event loop data structures */
  121. MyBoolean                    Eep_InitEventLoop(void)
  122.     {
  123.         OSErr                        Error;
  124.         long                        Result;
  125.  
  126.         /* allocate keyboard buffer.  if this fails & it returns NIL, it's ok. */
  127.         KeyboardEventBuffer = (EventRecord*)AllocPtrCanFail(0,"KeyboardEventBuffer");
  128.         /* figure out if we are running on a sleepy powerbook */
  129.         Error = Gestalt(gestaltPowerMgrAttr,&Result);
  130.         IsThisAPowerBook = ((Result & (1 << gestaltPMgrExists)) != 0) && (Error == noErr);
  131.         return True;
  132.     }
  133.  
  134.  
  135. /* dispose of any internal event loop structures */
  136. void                            Eep_ShutdownEventLoop(void)
  137.     {
  138.         if (KeyboardEventBuffer != NIL)
  139.             {
  140.                 ReleasePtr((char*)KeyboardEventBuffer);
  141.             }
  142.         KeyboardEventBuffer = NIL;
  143.     }
  144.  
  145.  
  146. /* returns the ID number of the current window.  Returns 0 if there are no windows */
  147. static WinType*    GetCurrentWindow(void)
  148.     {
  149.         if ((FrontWindow() != NIL) && RunningInForeground)
  150.             {
  151.                 CheckPtrExistence((WinType*)GetWRefCon(FrontWindow()));
  152.                 return (WinType*)GetWRefCon(FrontWindow());
  153.             }
  154.          else
  155.             {
  156.                 return NIL;
  157.             }
  158.     }
  159.  
  160.  
  161. /* local routine that converts Toolbox modifier flags to our own modifier flags */
  162. static short            FormModifiers(short Modifiers)
  163.     {
  164.         short                        Value;
  165.  
  166.         Value = 0;
  167.         if ((Modifiers & shiftKey) != 0)
  168.             {
  169.                 Value |= eShiftKey;
  170.             }
  171.         if ((Modifiers & controlKey) != 0)
  172.             {
  173.                 Value |= eControlKey;
  174.             }
  175.         if ((Modifiers & cmdKey) != 0)
  176.             {
  177.                 Value |= eCommandKey;
  178.             }
  179.         if ((Modifiers & optionKey) != 0)
  180.             {
  181.                 Value |= eOptionKey;
  182.             }
  183.         if ((Modifiers & alphaLock) != 0)
  184.             {
  185.                 Value |= eCapsLockKey;
  186.             }
  187.         if ((Modifiers & btnState) != 0)
  188.             {
  189.                 Value |= eMouseDownFlag;
  190.             }
  191.         return Value;
  192.     }
  193.  
  194.  
  195. /* Fetch an event from the event queue and return it.  Only some of the parameters */
  196. /* returned may be valid; see the enumeration comments above for EventType to see */
  197. /* which.  Mouse coordinates are always local to the current window, or undefined */
  198. /* if there is no current window.  If there are no events, the routine will return */
  199. /* after some amount of time.  This routine may call the Menu manager, so menus */
  200. /* should be initialized before this routine is called.  Any parameter may be passed */
  201. /* as NIL if the user doesn't care about the result.  Window changes do not occur */
  202. /* if the mouse is down.  If the current window is a dialog box, then a window */
  203. /* change will never be returned for another window.  Mouse up events are always */
  204. /* returned with the same window as the mosue down event, even if the mouse is no */
  205. /* longer in the window. */
  206. EventType                    GetAnEvent(OrdType* Xloc, OrdType* Yloc, ModifierFlags* Modifiers,
  207.                                         WinType** Window, MenuItemType** MenuCommand, char* KeyPressed)
  208.     {
  209.         /* the event.  statically allocated so that it is still valid when called */
  210.         /* again.  this is used for remembering the event that triggered a menu thing. */
  211.         static EventRecord        MyEvent;
  212.         /* state variable for menu handling */
  213.         static MenuStates            MenuState = eNoMenu;
  214.         /* when was the last menu selected (for slowing down menu bar flash) */
  215.         static unsigned long    WhenMenuWasSelected;
  216.  
  217.         WindowPtr                            WhichWindow;
  218.         long                                    MenuCommandInteger;
  219.  
  220.  
  221.         /* redraw the menu bar if it has changed */
  222.         Eep_RedrawMenuBar();
  223.  
  224.         /* the cancel pending flag for RelinquishCPU is sticky, so that if the user */
  225.         /* cancels once, it continues to return True.  It gets cleared the next time */
  226.         /* the program tries to handle a "real" event. */
  227.         CancelPending = False;
  228.  
  229.         /* redraw any windows that couldn't be redrawn when the event was received. */
  230.         PerformDeferredUpdates();
  231.  
  232.         /* main loop */
  233.      LoopPoint:
  234.  
  235.         /* check to see if active window has changed */
  236.         if (LastActiveWindow != FrontWindow())
  237.             {
  238.                 LastActiveWindow = FrontWindow();
  239.                 if (Window != NIL)
  240.                     {
  241.                         *Window = GetCurrentWindow();
  242.                     }
  243.                 return eActiveWindowChanged;
  244.             }
  245.  
  246.         /* if there are any queued up keypresses from RelinquishCPU, report them now */
  247.         if ((KeyboardEventBuffer != NIL) && (PtrSize((char*)KeyboardEventBuffer) > 0))
  248.             {
  249.                 EventRecord*    Temp;
  250.                 long                    OldSize;
  251.  
  252.                 CheckPtrExistence(KeyboardEventBuffer);
  253.                 /* a keypress was buffered during a RelinquishCPU call */
  254.                 MyEvent = KeyboardEventBuffer[0]; /* save the event */
  255.                 OldSize = PtrSize((char*)KeyboardEventBuffer);
  256.                 PRNGCHK(KeyboardEventBuffer,&(KeyboardEventBuffer[1]),
  257.                     OldSize - sizeof(EventRecord));
  258.                 PRNGCHK(KeyboardEventBuffer,&(KeyboardEventBuffer[0]),
  259.                     OldSize - sizeof(EventRecord));
  260.                 MoveData((char*)&(KeyboardEventBuffer[1]),(char*)&(KeyboardEventBuffer[0]),
  261.                     OldSize - sizeof(EventRecord));
  262.                 Temp = (EventRecord*)ResizePtr((char*)KeyboardEventBuffer,
  263.                     OldSize - sizeof(EventRecord));
  264.                 if (Temp == NIL)
  265.                     {
  266.                         /* if we run out of memory, then we just lose all the buffered keypresses */
  267.                         ReleasePtr((char*)KeyboardEventBuffer);
  268.                         KeyboardEventBuffer = (EventRecord*)AllocPtrCanFail(0,"KeyboardEventBuffer");
  269.                     }
  270.                  else
  271.                     {
  272.                         KeyboardEventBuffer = Temp;
  273.                     }
  274.                 /* now, jump so that we fake the event */
  275.                 goto HandleEventSwitchPoint;
  276.             }
  277.  
  278.         /* handle any menu operations that are in progress. */
  279.         switch (MenuState)
  280.             {
  281.                 default:
  282.                     EXECUTE(PRERR(ForceAbort,"GetAnEvent:  bad MenuState"));
  283.                     break;
  284.                 case eNoMenu:
  285.                     break; /* continue on through */
  286.                 case eMenuPendingMouse:
  287.                     /* let system process event */
  288.                     /* MyEvent is still valid from last time through */
  289.                     MenuCommandInteger = MenuSelect(MyEvent.where);
  290.                     /* if the menu was actually chosen, then return it */
  291.                     if (MenuCommand != NIL)
  292.                         {
  293.                             *MenuCommand = Eep_MMID2ItemID(MenuCommandInteger);
  294.                             if (*MenuCommand != NIL)
  295.                                 {
  296.                                     /* we only report a menu choice if the user actually chose something */
  297.                                     MenuState = eMenuSelected;
  298.                                     WhenMenuWasSelected = TickCount();
  299.                                     if (Modifiers != NIL)
  300.                                         {
  301.                                             *Modifiers = (ModifierFlags)FormModifiers(MyEvent.modifiers);
  302.                                         }
  303.                                     if (Window != NIL)
  304.                                         {
  305.                                             *Window = GetCurrentWindow();
  306.                                         }
  307.                                     MenuState = eMenuSelected;
  308.                                     return eMenuCommand;
  309.                                 }
  310.                         }
  311.                     /* if menu wasn't chosen, then reset menu state & get another event */
  312.                     MenuState = eNoMenu;
  313.                     goto LoopPoint;
  314.                 case eMenuPendingKey:
  315.                     /* convert the key to a menu thing */
  316.                     /* MyEvent is still valid from last time through */
  317.                     MenuCommandInteger = MenuKey(MyEvent.message & charCodeMask);
  318.                     /* convert the menu command into something we can handle */
  319.                     if (MenuCommand != NIL)
  320.                         {
  321.                             /* only do menus if they can handle them */
  322.                             *MenuCommand = Eep_MMID2ItemID(MenuCommandInteger);
  323.                             /* if the menu actually happened, then report it */
  324.                             if (*MenuCommand != NIL)
  325.                                 {
  326.                                     WhenMenuWasSelected = TickCount();
  327.                                     if (Modifiers != NIL)
  328.                                         {
  329.                                             *Modifiers = (ModifierFlags)FormModifiers(MyEvent.modifiers);
  330.                                         }
  331.                                     if (Window != NIL)
  332.                                         {
  333.                                             *Window = GetCurrentWindow();
  334.                                         }
  335.                                     MenuState = eMenuSelected;
  336.                                     return eMenuCommand;
  337.                                 }
  338.                         }
  339.                     /* if the key doesn't correspond to a menu item, then schlep on over */
  340.                     /* to the normal keypress handler */
  341.                     MenuState = eNoMenu;
  342.                     goto FinishKeypressEvent;
  343.                     break;
  344.                 case eMenuSelected:
  345.                     /* make a small delay so that the menu is actually visible */
  346.                     while (TickCount() - WhenMenuWasSelected < MENUDELAY)
  347.                         {
  348.                             /* hideous delay loop to flash menu so user can see it. */
  349.                         }
  350.                     HiliteMenu(0);
  351.                     MenuState = eNoMenu;
  352.                     break;
  353.             }
  354.  
  355.         /* call the event routine.  the ugly parameter decides whether a mouse is */
  356.         /* down and uses that to speed up mouse tracking */
  357.         WaitNextEvent(everyEvent,&MyEvent,((LastMouseDownInThisWindow != NIL)
  358.             && (SleepTime > VERYSMALLTIMEINTERVAL)) ? VERYSMALLTIMEINTERVAL : SleepTime,NIL);
  359.  
  360.         /* remember last event time for RelinquishCPU */
  361.         LastEventTime = TickCount();
  362.  
  363.         /* decode the event */
  364.      HandleEventSwitchPoint:
  365.         switch (MyEvent.what)
  366.             {
  367.                 case nullEvent:
  368.                     if (TickCount() - LastCursorCheck >= CURSORUPDATEDELAY)
  369.                         {
  370.                             if (FrontWindow() != NIL)
  371.                                 {
  372.                                     LastCursorCheck = TickCount();
  373.                                     SetPort(FrontWindow());
  374.                                     GlobalToLocal(&MyEvent.where);
  375.                                     if (Xloc != NIL)
  376.                                         {
  377.                                             *Xloc = MyEvent.where.h;
  378.                                         }
  379.                                     if (Yloc != NIL)
  380.                                         {
  381.                                             *Yloc = MyEvent.where.v;
  382.                                         }
  383.                                     if (Modifiers != NIL)
  384.                                         {
  385.                                             *Modifiers = (ModifierFlags)FormModifiers(MyEvent.modifiers);
  386.                                         }
  387.                                     if (Window != NIL)
  388.                                         {
  389.                                             *Window = GetCurrentWindow();
  390.                                         }
  391.                                     return eCheckCursor;
  392.                                 }
  393.                              else
  394.                                 {
  395.                                     SetCursor(&qd.arrow);
  396.                                 }
  397.                         }
  398.                     if (FrontWindow() != NIL)
  399.                         {
  400.                             SetPort(FrontWindow());
  401.                             GlobalToLocal(&MyEvent.where);
  402.                             if (Xloc != NIL)
  403.                                 {
  404.                                     *Xloc = MyEvent.where.h;
  405.                                 }
  406.                             if (Yloc != NIL)
  407.                                 {
  408.                                     *Yloc = MyEvent.where.v;
  409.                                 }
  410.                         }
  411.                     if (Modifiers != NIL)
  412.                         {
  413.                             *Modifiers = (ModifierFlags)FormModifiers(MyEvent.modifiers);
  414.                         }
  415.                     if (Window != NIL)
  416.                         {
  417.                             *Window = GetCurrentWindow();
  418.                         }
  419.                     return eNoEvent;
  420.  
  421.                 case mouseDown:
  422.                     switch (FindWindow(MyEvent.where,&WhichWindow))
  423.                         {
  424.                             case inSysWindow:
  425.                                 EXECUTE(PRERR(AllowResume,"GetAnEvent:  FindWindow returned inSysWindow"));
  426.                                 /* SystemClick(&MyEvent,WhichWindow); */
  427.                                 goto LoopPoint;
  428.                             case inMenuBar:
  429.                                 MenuState = eMenuPendingMouse;
  430.                                 WipeMenusClean();
  431.                                 if (Modifiers != NIL)
  432.                                     {
  433.                                         *Modifiers = (ModifierFlags)FormModifiers(MyEvent.modifiers);
  434.                                     }
  435.                                 if (Window != NIL)
  436.                                     {
  437.                                         *Window = GetCurrentWindow();
  438.                                     }
  439.                                 return eMenuStarting;
  440.                             case inDrag:
  441.                                 if ((FrontWindow() != NIL) /* make sure there's a front window */
  442.                                     && (
  443.                                         /* front window must be a document window */
  444.                                         (GetWindowKind((WinType*)GetWRefCon(FrontWindow())) == eDocumentWindow)
  445.                                         /* or front window must be the window in question */
  446.                                         || (FrontWindow() == WhichWindow)
  447.                                         /* or command key must be down to prevent window switch */
  448.                                         || ((MyEvent.modifiers & cmdKey) != 0)))
  449.                                     {
  450.                                         Rect                    BoundsRect;
  451.                                         RgnHandle            BoundsRegion;
  452.  
  453.                                         BoundsRegion = GetGrayRgn();
  454.                                         BoundsRect = (**BoundsRegion).rgnBBox;
  455.                                         InsetRect(&BoundsRect,4,4);
  456.                                         DragWindow(WhichWindow,MyEvent.where,&BoundsRect);
  457.                                     }
  458.                                  else
  459.                                     {
  460.                                         ErrorBeep();
  461.                                     }
  462.                                 goto LoopPoint;
  463.                             case inContent:
  464.                             case inGrow:
  465.                                 if (FrontWindow() != WhichWindow)
  466.                                     {
  467.                                         if ((FrontWindow() == NIL) || (GetWindowKind(
  468.                                             (WinType*)GetWRefCon(FrontWindow())) == eDocumentWindow))
  469.                                             {
  470.                                                 SelectWindow(WhichWindow);
  471.                                             }
  472.                                          else
  473.                                             {
  474.                                                 ErrorBeep();
  475.                                             }
  476.                                         goto LoopPoint;
  477.                                     }
  478.                                 LastMouseDownInThisWindow = WhichWindow;
  479.                                 SetPort(WhichWindow);
  480.                                 GlobalToLocal(&MyEvent.where);
  481.                                 if (Modifiers != NIL)
  482.                                     {
  483.                                         *Modifiers = (ModifierFlags)FormModifiers(MyEvent.modifiers);
  484.                                     }
  485.                                 if (Xloc != NIL)
  486.                                     {
  487.                                         *Xloc = MyEvent.where.h;
  488.                                     }
  489.                                 if (Yloc != NIL)
  490.                                     {
  491.                                         *Yloc = MyEvent.where.v;
  492.                                     }
  493.                                 if (Window != NIL)
  494.                                     {
  495.                                         *Window = (WinType*)GetWRefCon(WhichWindow);
  496.                                     }
  497.                                 return eMouseDown;
  498.                             case inGoAway:
  499.                                 if (TrackGoAway(WhichWindow,MyEvent.where))
  500.                                     {
  501.                                         if (Window != NIL)
  502.                                             {
  503.                                                 *Window = (WinType*)GetWRefCon(WhichWindow);
  504.                                             }
  505.                                         return eWindowClosing;
  506.                                     }
  507.                                 goto LoopPoint;
  508.                             case inZoomIn:
  509.                                 SetPort(WhichWindow);
  510.                                 if (TrackBox(WhichWindow,MyEvent.where,inZoomIn))
  511.                                     {
  512.                                         ZoomWindow(WhichWindow,inZoomIn,False);
  513.                                         if (Window != NIL)
  514.                                             {
  515.                                                 *Window = (WinType*)GetWRefCon(WhichWindow);
  516.                                             }
  517.                                         return eWindowResized;
  518.                                     }
  519.                                 goto LoopPoint;
  520.                             case inZoomOut:
  521.                                 SetPort(WhichWindow);
  522.                                 if (TrackBox(WhichWindow,MyEvent.where,inZoomOut))
  523.                                     {
  524.                                         ZoomWindow(WhichWindow,inZoomOut,False);
  525.                                         if (Window != NIL)
  526.                                             {
  527.                                                 *Window = (WinType*)GetWRefCon(WhichWindow);
  528.                                             }
  529.                                         return eWindowResized;
  530.                                     }
  531.                                 goto LoopPoint;
  532.                             default:
  533.                                 goto LoopPoint;
  534.                         }
  535.                     break;
  536.  
  537.                 case mouseUp:
  538.                     if (LastMouseDownInThisWindow == NIL)
  539.                         {
  540.                             /* orphaned mouse-up, probably munched during RelinquishCPU */
  541.                             goto LoopPoint;
  542.                         }
  543.                     CheckPtrExistence((WinType*)GetWRefCon(LastMouseDownInThisWindow));
  544.                     SetPort(LastMouseDownInThisWindow);
  545.                     GlobalToLocal(&MyEvent.where);
  546.                     if (Modifiers != NIL)
  547.                         {
  548.                             *Modifiers = (ModifierFlags)FormModifiers(MyEvent.modifiers);
  549.                         }
  550.                     if (Xloc != NIL)
  551.                         {
  552.                             *Xloc = MyEvent.where.h;
  553.                         }
  554.                     if (Yloc != NIL)
  555.                         {
  556.                             *Yloc = MyEvent.where.v;
  557.                         }
  558.                     if (Window != NIL)
  559.                         {
  560.                             *Window = (WinType*)GetWRefCon(LastMouseDownInThisWindow);
  561.                         }
  562.                     LastMouseDownInThisWindow = NIL;
  563.                     return eMouseUp;
  564.  
  565.                 case keyDown:
  566.                 case autoKey:
  567. #if DEBUG
  568.                     if ((MyEvent.modifiers & cmdKey) && (MyEvent.modifiers & shiftKey)
  569.                         && ((MyEvent.message & charCodeMask) == 'h'))
  570.                         {
  571.                             CheckFragmentation();
  572.                             goto LoopPoint;
  573.                         }
  574. #endif
  575.                     if ((MyEvent.modifiers & cmdKey) != 0)
  576.                         {
  577.                             if ((MyEvent.message & charCodeMask) == '.')
  578.                                 {
  579.                                     if (KeyPressed != NIL)
  580.                                         {
  581.                                             *KeyPressed = eCancelKey;
  582.                                         }
  583.                                     goto KeypressCancelSkipCode;
  584.                                 }
  585.                             MenuState = eMenuPendingKey;
  586.                             WipeMenusClean();
  587.                             if (Modifiers != NIL)
  588.                                 {
  589.                                     *Modifiers = (ModifierFlags)FormModifiers(MyEvent.modifiers);
  590.                                 }
  591.                             if (Window != NIL)
  592.                                 {
  593.                                     *Window = GetCurrentWindow();
  594.                                 }
  595.                             return eMenuStarting;
  596.                         }
  597.                     /* the menu keypress handler jumps here to handle commanded keys */
  598.                  FinishKeypressEvent:
  599.                     if (KeyPressed != NIL)
  600.                         {
  601.                             *KeyPressed = MyEvent.message & charCodeMask;
  602.                         }
  603.                     /* jump here if *KeyPressed is eCancelKey and you don't want */
  604.                     /* to clobber it by storing something else in it */
  605.                  KeypressCancelSkipCode:
  606.                     if (FrontWindow() != NIL)
  607.                         {
  608.                             SetPort(FrontWindow());
  609.                             GlobalToLocal(&MyEvent.where);
  610.                             if (Xloc != NIL)
  611.                                 {
  612.                                     *Xloc = MyEvent.where.h;
  613.                                 }
  614.                             if (Yloc != NIL)
  615.                                 {
  616.                                     *Yloc = MyEvent.where.v;
  617.                                 }
  618.                         }
  619.                     if (Modifiers != NIL)
  620.                         {
  621.                             *Modifiers = (ModifierFlags)FormModifiers(MyEvent.modifiers);
  622.                         }
  623.                     if (Window != NIL)
  624.                         {
  625.                             *Window = GetCurrentWindow();
  626.                         }
  627.                     return eKeyPressed;
  628.  
  629.                 case keyUp:
  630.                     goto LoopPoint;
  631.  
  632.                 case updateEvt:
  633.                     BeginUpdate((WindowPtr)MyEvent.message);
  634.                     CallWindowUpdate((WinType*)GetWRefCon((WindowPtr)MyEvent.message));
  635.                     EndUpdate((WindowPtr)MyEvent.message);
  636.                     goto LoopPoint;
  637.  
  638.                 case activateEvt:
  639.                     /* we'll catch this when we test FrontWindow() */
  640.                     goto LoopPoint;
  641.  
  642.                 case osEvt:
  643.                     switch ((unsigned char)((MyEvent.message >> 24) & 0x000000ff))
  644.                         {
  645.                             case suspendResumeMessage:
  646.                                 if (!(MyEvent.message & resumeFlag))
  647.                                     {
  648.                                         /* suspend */
  649.                                         JudiciousInterval = JUDICIOUSDELAYBACKGROUND;
  650.                                         RunningInForeground = False;
  651.                                         if (Window != NIL)
  652.                                             {
  653.                                                 *Window = 0;
  654.                                             }
  655.                                     }
  656.                                  else
  657.                                     {
  658.                                         /* resume */
  659.                                         JudiciousInterval = JUDICIOUSDELAYFOREGROUND;
  660.                                         RunningInForeground = True;
  661.                                         if (Window != NIL)
  662.                                             {
  663.                                                 *Window = GetCurrentWindow();
  664.                                             }
  665.                                     }
  666.                                 return eActiveWindowChanged;
  667.                             case mouseMovedMessage:
  668.                                 break;
  669.                             default:
  670.                                 break;
  671.                         }
  672.                     goto LoopPoint;
  673.  
  674.                 case kHighLevelEvent:
  675.                     AEProcessAppleEvent(&MyEvent);
  676.                     goto LoopPoint;
  677.  
  678.                 default:
  679.                     goto LoopPoint;
  680.             }
  681.     }
  682.  
  683.  
  684. /* set the amount of time to wait for an event.  The default is 1/4 of a second */
  685. /* time units are in seconds (can be fractional since they are floating point values) */
  686. double                SetEventSleepTime(double TheSleepTime)
  687.     {
  688.         unsigned long            OldSleepTime;
  689.  
  690.         OldSleepTime = SleepTime;
  691.         SleepTime = (TheSleepTime * 60) + 0.5;
  692.         return (double)OldSleepTime / 60 + 0.5;
  693.     }
  694.  
  695.  
  696. /* used by RelinquishCPUCheckCancel.  RelinquishCPUCheckCancel continually */
  697. /* resets it's value to VERYSMALLTIMEINVERVAL, but if it is changed, then */
  698. /* the new delay will take effect for one call of RelinquishCPUCheckCancel. */
  699. /* RelinquishCPUJudiciouslyCheckCancel uses this to get the processor back */
  700. /* as soon as possible. */
  701. static long                    RelinqCPUDelay = RELINQUISHCPUNORMALDELAY;
  702.  
  703. /* relinquishes CPU for 1 tick and checks to see if the user hit escape or cmd-. */
  704. /* (or perhaps other cancel signals) and returns True if the user is trying to */
  705. /* cancel.  Through the use of the global variable CancelPending, cancels are */
  706. /* "sticky" so that subsequent calls will return True as well, until GetAnEvent is */
  707. /* called. */
  708. MyBoolean            RelinquishCPUCheckCancel(void)
  709.     {
  710.         EventRecord            StupidEvent;
  711.         WindowPtr                WhichWindow;
  712.  
  713.         if (LastMouseDownInThisWindow != NIL)
  714.             {
  715.                 /* since we munch mouse events, we don't want to do this during a mouse */
  716.                 /* down since we'll probably lose the mouse up.  It's rather silly for the */
  717.                 /* user to hit cancel while holding the mouse down anyway... */
  718.                 return CancelPending;
  719.             }
  720.      TryAgainPoint:
  721.         WaitNextEvent(keyDownMask | keyUpMask | mDownMask | mUpMask | updateMask
  722.             | osMask | autoKeyMask | diskMask,&StupidEvent,RelinqCPUDelay,NIL);
  723.         LastEventTime = TickCount();
  724.         switch (StupidEvent.what)
  725.             {
  726.                 case updateEvt:
  727.                     BeginUpdate((WindowPtr)StupidEvent.message);
  728.                     MarkForDeferredUpdate((WinType*)GetWRefCon((WindowPtr)StupidEvent.message));
  729.                     EndUpdate((WindowPtr)StupidEvent.message);
  730.                     goto TryAgainPoint;
  731.                 case mouseDown:
  732.                     switch (FindWindow(StupidEvent.where,&WhichWindow))
  733.                         {
  734.                             default:
  735.                                 break;
  736.                             case inSysWindow:
  737.                                 EXECUTE(PRERR(AllowResume,"GetAnEvent:  FindWindow returned inSysWindow"));
  738.                                 /* SystemClick(&StupidEvent,WhichWindow); */
  739.                                 break;
  740.                             case inDrag:
  741.                                 if ((FrontWindow() == WhichWindow) || ((FrontWindow() != NIL)
  742.                                     && ((GetWindowKind((WinType*)GetWRefCon(FrontWindow()))
  743.                                     == eDocumentWindow) || (GetWindowKind((WinType*)GetWRefCon(
  744.                                     FrontWindow())) == eModelessDialogWindow))
  745.                                     && ((StupidEvent.modifiers & cmdKey) != 0)))
  746.                                     {
  747.                                         Rect                    BoundsRect;
  748.                                         RgnHandle            BoundsRegion;
  749.  
  750.                                         BoundsRegion = GetGrayRgn();
  751.                                         BoundsRect = (**BoundsRegion).rgnBBox;
  752.                                         InsetRect(&BoundsRect,4,4);
  753.                                         DragWindow(WhichWindow,StupidEvent.where,&BoundsRect);
  754.                                     }
  755.                                  else
  756.                                     {
  757.                                         ErrorBeep();
  758.                                     }
  759.                                 break;
  760.                         }
  761.                     break;
  762.                 case keyDown:
  763.                     if ((((StupidEvent.message & charCodeMask) == '.')
  764.                         && ((StupidEvent.modifiers & cmdKey) != 0)) ||
  765.                         ((StupidEvent.message & charCodeMask) == 27))
  766.                         {
  767.                             CancelPending = True; /* stick */
  768.                         }
  769.                     else if (KeyboardEventBuffer != NIL)
  770.                         {
  771.                             EventRecord*        Temp;
  772.                             long                        OldSize;
  773.  
  774.                             CheckPtrExistence(KeyboardEventBuffer);
  775.                             /* maybe the keypress was wanted, so we should save it */
  776.                             OldSize = PtrSize((char*)KeyboardEventBuffer);
  777.                             Temp = (EventRecord*)ResizePtr((char*)KeyboardEventBuffer,
  778.                                 OldSize + sizeof(EventRecord));
  779.                             if (Temp != NIL)
  780.                                 {
  781.                                     Temp[OldSize / sizeof(EventRecord)] = StupidEvent;
  782.                                     KeyboardEventBuffer = Temp;
  783.                                 }
  784.                         }
  785.                     break;
  786.                 default:
  787.                     break;
  788.             }
  789.         RelinqCPUDelay = RELINQUISHCPUNORMALDELAY;
  790.         return CancelPending; /* sticky */
  791.     }
  792.  
  793.  
  794. /* similar to RelinquishCPUCheckCancel but in cooperative multitasking systems, */
  795. /* it gives much better performance for the application by not releasing the */
  796. /* processor nearly as often */
  797. MyBoolean            RelinquishCPUJudiciouslyCheckCancel(void)
  798.     {
  799.         if (TickCount() - LastEventTime < JudiciousInterval)
  800.             {
  801.                 return CancelPending; /* sticky */
  802.             }
  803.         RelinqCPUDelay = 0; /* come back right away */
  804.         if (IsThisAPowerBook)
  805.             {
  806.                 /* if some CPU intensive task is going on, then keep the processor */
  807.                 /* from getting sleepy */
  808.                 IdleUpdate();
  809.             }
  810.         return RelinquishCPUCheckCancel();
  811.     }
  812.  
  813.  
  814. /* read the system timer (in seconds).  The timer returns real time (not process */
  815. /* time) but not relative to any known time.  The value may roll over from an */
  816. /* undefined large number to 0. */
  817. double                ReadTimer(void)
  818.     {
  819.         return ((double)TickCount()) / 60;
  820.     }
  821.  
  822.  
  823. /* find the true difference between two timer values even if one has rolled over */
  824. double                TimerDifference(double Now, double Then)
  825.     {
  826.         return (double)((unsigned long)(60 * Now + 0.5)
  827.             - (unsigned long)(60 * Then + 0.5)) / 60;
  828.     }
  829.  
  830.  
  831. /* get the current mouse position.  If there is no current window, the */
  832. /* results are undefined.  Either of the parameters can be NIL if the user */
  833. /* doesn't care about the result */
  834. void                    GetMousePosition(OrdType* Xloc, OrdType* Yloc)
  835.     {
  836.         Point            MouseLoc;
  837.  
  838.         if (FrontWindow() != NIL)
  839.             {
  840.                 SetPort(FrontWindow());
  841.                 GetMouse(&MouseLoc);
  842.                 if (Xloc != NIL)
  843.                     {
  844.                         *Xloc = MouseLoc.h;
  845.                     }
  846.                 if (Yloc != NIL)
  847.                     {
  848.                         *Yloc = MouseLoc.v;
  849.                     }
  850.             }
  851.     }
  852.  
  853.  
  854. /* read the state of the modifier keys on the keyboard.  On systems that don't */
  855. /* allow this, the function may return the modifiers as they were at the last */
  856. /* known time */
  857. ModifierFlags    CheckModifiers(void)
  858.     {
  859.         EventRecord            StupidEvent;
  860.  
  861.         WaitNextEvent(0,&StupidEvent,0,NIL);
  862.         return (ModifierFlags)FormModifiers(StupidEvent.modifiers);
  863.     }
  864.  
  865.  
  866. /* set an implementation defined version of the specified cursor */
  867. void                    SetArrowCursor(void)
  868.     {
  869.         SetCursor(&qd.arrow);
  870.     }
  871.  
  872.  
  873. /* set an implementation defined version of the specified cursor */
  874. void                    SetIBeamCursor(void)
  875.     {
  876.         CursHandle        DaCursor;
  877.  
  878.         DaCursor = GetCursor(iBeamCursor);
  879.         HLock((Handle)DaCursor);
  880.         SetCursor(*DaCursor);
  881.         HUnlock((Handle)DaCursor);
  882.     }
  883.  
  884.  
  885. /* set an implementation defined version of the specified cursor */
  886. void                    SetWatchCursor(void)
  887.     {
  888.         CursHandle        DaCursor;
  889.  
  890.         DaCursor = GetCursor(watchCursor);
  891.         HLock((Handle)DaCursor);
  892.         SetCursor(*DaCursor);
  893.         HUnlock((Handle)DaCursor);
  894.     }
  895.  
  896.  
  897. /* set an implementation defined version of the specified cursor */
  898. void                    SetCrossHairCursor(void)
  899.     {
  900.         CursHandle        DaCursor;
  901.  
  902.         DaCursor = GetCursor(crossCursor);
  903.         HLock((Handle)DaCursor);
  904.         SetCursor(*DaCursor);
  905.         HUnlock((Handle)DaCursor);
  906.     }
  907.  
  908.  
  909. /* set the cursor tho the image and mask specified.  If the implementation's cursor */
  910. /* is larger than 16x16, then the adjustment is implementation defined.  On the */
  911. /* Macintosh, cursors are 16x16, so no adjustment is necessary */
  912. /* the most significant bit of the word is leftmost */
  913. void                            SetTheCursor(short HotPointX, short HotPointY,
  914.                                         unsigned short CursorImage[16], unsigned short CursorMask[16])
  915.     {
  916.         Cursor                    TheCurs;
  917.         int                            Scan;
  918.  
  919.         ERROR((HotPointX < 0) || (HotPointX >= 16) || (HotPointY < 0) || (HotPointY >= 16),
  920.             PRERR(AllowResume,"SetTheCursor:  hot point out of range"));
  921.         for (Scan = 0; Scan < 16; Scan += 1)
  922.             {
  923.                 TheCurs.data[Scan] = CursorImage[Scan];
  924.                 TheCurs.mask[Scan] = CursorMask[Scan];
  925.             }
  926.         TheCurs.hotSpot.h = HotPointX;
  927.         TheCurs.hotSpot.v = HotPointY;
  928.         SetCursor(&TheCurs);
  929.     }
  930.  
  931.  
  932. /* get the number of seconds to wait before toggling an insertion point */
  933. double                GetCursorBlinkRate(void)
  934.     {
  935.         return ((double)GetCaretTime()) / 60;
  936.     }
  937.  
  938.  
  939. /* get the maximum time between clicks for which they are considered a double click */
  940. double                GetDoubleClickInterval(void)
  941.     {
  942.         return ((double)GetDblTime()) / 60;
  943.     }
  944.  
  945.  
  946. /* emit a not too annoying beep to indicate an error occurred */
  947. void                    ErrorBeep(void)
  948.     {
  949.         if (MakeErrorBeeps)
  950.             {
  951.                 SysBeep(BEEPDURATION);
  952.             }
  953.          else
  954.             {
  955.                 unsigned long            StartTime;
  956.  
  957.                 FlashMenuBar(0);
  958.                 StartTime = TickCount();
  959.                 while (TickCount() - StartTime < MENUDELAY)
  960.                     {
  961.                         /* hideous delay loop to flash menu bar so user can see it. */
  962.                         RelinquishCPUCheckCancel();
  963.                     }
  964.                 FlashMenuBar(0);
  965.             }
  966.     }
  967.  
  968.  
  969. /* enable or disable error beeping.  True enables it.  the old value is returned. */
  970. MyBoolean                    SetErrorBeepEnable(MyBoolean ShouldWeBeep)
  971.     {
  972.         MyBoolean                OldValue;
  973.  
  974.         OldValue = MakeErrorBeeps;
  975.         MakeErrorBeeps = ShouldWeBeep;
  976.         return OldValue;
  977.     }
  978.  
  979.  
  980. /* this routine is called when a window is dying, so any locally cached pointers */
  981. /* to windows have to be discarded */
  982. void                    Eep_WindowDying(WinType* Window)
  983.     {
  984.         if ((WinType*)GetWRefCon(LastMouseDownInThisWindow) == Window)
  985.             {
  986.                 LastMouseDownInThisWindow = NIL;
  987.             }
  988.         if ((WinType*)GetWRefCon(LastActiveWindow) == Window)
  989.             {
  990.                 LastActiveWindow = NIL;
  991.             }
  992.     }
  993.